//---cat rawtcp.c---
// Run as root or SUID 0, just datagram no data/payload
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

// Packet length
#define PCKT_LEN 8192

// Simple checksum function, may use others such as Cyclic Redundancy Check, CRC
unsigned short csum(unsigned short *buf, int len)
{
    unsigned long sum;
    for(sum=0; len>0; len--)
        sum += *buf++;
    sum = (sum >> 16) + (sum &0xffff);
    sum += (sum >> 16);
    return (unsigned short)(~sum);
}

void fill_packet(char *argv[], char *buffer)
{
    struct iphdr *ip = (struct iphdr *)buffer;
    struct tcphdr *tcp = (struct tcphdr *)(buffer + sizeof(struct iphdr));
    char *opt = (char *)tcp + sizeof(struct tcphdr);

    memset(buffer, 0, PCKT_LEN);

    // IP structure
    ip->ihl = 5;
    ip->version = 4;
    ip->tos = 0;
    ip->tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr);
    ip->id = htons(54321);
    ip->frag_off = 0;
    ip->ttl = 64;
    ip->protocol = 6; // TCP
    ip->check = 0; // Done by kernel

    // Source IP, modify as needed, spoofed, we accept through command line argument
    //ip->saddr = inet_addr("1.2.3.4");
    ip->saddr = inet_addr(argv[1]);
    // Destination IP, modify as needed, but here we accept through command line argument
    //ip->daddr = inet_addr ("60.61.62.63");
    ip->daddr = inet_addr (argv[3]);

    //ip->daddr = inet_addr(argv[3]);

    // The TCP structure. The source port, spoofed, we accept through the command line
    tcp->source = htons(atoi(argv[2]));
    // The destination port, we accept through command line
    tcp->dest = htons(atoi(argv[4]));
    tcp->seq = htonl(1);
    tcp->ack_seq = 0;
    tcp->doff = 0;
    tcp->syn = 0;
    tcp->window = htons(32767);
    tcp->check = 0; // Done by kernel
    tcp->urg_ptr = 0;
    // IP checksum calculation
    ip->check = csum((unsigned short *) buffer, ip->tot_len >> 1);

    opt[0] = 'c';
    opt[1] = 'c';
    opt[2] = 'm';
    opt[3] = 'a';
}

void update_port(char *buffer, unsigned port)
{
    struct tcphdr *tcp = (struct tcphdr *)(buffer + sizeof(struct iphdr));
    tcp->source = htons(port%65535);
}

void make_syn(char *buffer)
{
    struct tcphdr *tcp = (struct tcphdr *)(buffer + sizeof(struct iphdr));
    tcp->syn = 1;
    tcp->ack = 0;
    tcp->fin = 0;
}

void make_ack(char *buffer)
{
    struct tcphdr *tcp = (struct tcphdr *)(buffer + sizeof(struct iphdr));
    tcp->syn = 0;
    tcp->ack = 1;
    tcp->fin = 0;
}

void make_fin(char *buffer)
{
    struct tcphdr *tcp = (struct tcphdr *)(buffer + sizeof(struct iphdr));
    tcp->syn = 0;
    tcp->ack = 0;
    tcp->fin = 1;
}

int main(int argc, char *argv[])
{
    int sd;
    // No data, just datagram
    char buffer[PCKT_LEN];
    int one = 1;
    const int *val = &one;
    int tot_len = sizeof(struct iphdr) + sizeof(struct tcphdr) + 4;
    struct sockaddr_in din;
    
    memset(buffer, 0, PCKT_LEN);

    if(argc != 6)
    {
        printf("- Invalid parameters!!!\n");
        printf("- Usage: %s <source hostname/IP> <source port> <target hostname/IP> <target port> <#of conn>\n", argv[0]);
        exit(-1);
    }

    din.sin_family = AF_INET;
    din.sin_port = htons(atoi(argv[4]));
    din.sin_addr.s_addr = inet_addr(argv[3]);
    
    fill_packet(argv, buffer);

    sd = socket(PF_INET, SOCK_RAW, IPPROTO_TCP);
    if(sd < 0)
    {
        perror("socket() error");
        exit(-1);
    }
    else
        printf("socket()-SOCK_RAW and tcp protocol is OK.\n");

    // Inform the kernel do not fill up the headers' structure, we fabricated our own
    if(setsockopt(sd, IPPROTO_IP, IP_HDRINCL, val, sizeof(one)) < 0)
    {
        perror("setsockopt() error");
        exit(-1);
    }
    else
        printf("setsockopt() is OK\n");

    printf("Using:::::Source IP: %s port: %u, Target IP: %s port: %u.\n", argv[1], atoi(argv[2]), argv[3], atoi(argv[4]));

    // sendto() loop, send every 2 second for 50 counts
    unsigned int count, total = atoi(argv[5]);
    struct timeval start, end;
    unsigned long long start_utime, end_utime, total_utime;

    gettimeofday(&start, NULL);
    for(count = 0; count < total; count++)
    {
        update_port(buffer, count+1);
        /* syn */
        make_syn(buffer);
        if(sendto(sd, buffer, tot_len, MSG_DONTWAIT, (struct sockaddr *)&din, sizeof(din)) < 0)
            // Verify
        {
            perror("sendto() error");
            exit(-1);
        }

        /* ack */
        make_ack(buffer);
        if(sendto(sd, buffer, tot_len, MSG_DONTWAIT, (struct sockaddr *)&din, sizeof(din)) < 0)
            // Verify
        {
            perror("sendto() error");
            exit(-1);
        }

        /* fin */
        make_fin(buffer);
        if(sendto(sd, buffer, tot_len, MSG_DONTWAIT, (struct sockaddr *)&din, sizeof(din)) < 0)
            // Verify
        {
            perror("sendto() error");
            exit(-1);
        }

        /* ack */
        make_ack(buffer);
        if(sendto(sd, buffer, tot_len, MSG_DONTWAIT, (struct sockaddr *)&din, sizeof(din)) < 0)
            // Verify
        {
            perror("sendto() error");
            exit(-1);
        }
    }
    gettimeofday(&end, NULL);
    close(sd);

    start_utime = start.tv_sec * 1000000 + start.tv_usec;
    end_utime = end.tv_sec * 1000000 + end.tv_usec;
    total_utime = end_utime - start_utime;
    printf("runtime: %llu usec\n", total_utime);

    printf("each connection : %f conn/sec\n", ((double)total/(double)total_utime)*1000000);
    return 0;
}
